use std::fs;
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;

use serde::{self, Serialize};
use x_hash::{DigestFmt, Md5};
use x_io::{directory as XDirectory, file as XFile};

#[derive(Serialize, Debug)]
#[serde(rename_all = "snake_case")]
struct MetaData {
    size: u64,
    is_file: bool,
    is_exist: bool,
    is_directory: bool,
}

/// 检测文件状态
#[tauri::command]
async fn state(path: &Path) -> Result<MetaData, String> {
    let mut data = MetaData { size: 0, is_file: false, is_exist: false, is_directory: false };
    if path.exists() {
        let size = if path.is_file() {
            fs::metadata(&path).unwrap().len()
        } else {
            0
        };
        data.size = size;
        data.is_file = path.is_file();
        data.is_exist = true;
        data.is_directory = path.is_dir();
        return Ok(data);
    }
    return Ok(data);
}

/// 检测文件是否存在
#[tauri::command]
async fn check_exist(path: &Path) -> Result<bool, String> {
    if path.exists() {
        return Ok(true);
    } else {
        return Err("Path does not exist".to_string());
    }
}

/// 确保文件存在, 不存在会创建一个空文件
#[tauri::command]
async fn ensure_exist(path: &Path) -> Result<bool, String> {
    return match XFile::ensure_exist(path) {
        Ok(_) => Ok(true),
        Err(_) => Err("Path processing failed".to_string()),
    };
}

/// 删除文件/文件夹
#[tauri::command]
async fn remove(path: &Path) -> Result<bool, String> {
    if path.is_file() {
        return match XFile::delete(path) {
            Ok(_) => Ok(true),
            Err(_) => Err("Delete file failed".to_string()),
        };
    }
    if path.is_dir() {
        return match XDirectory::delete(path) {
            Ok(_) => Ok(true),
            Err(_) => Err("Delete directory failed".to_string()),
        };
    }
    return Err("Delete path failed".to_string());
}

/// 文件/文件夹重命名
#[tauri::command]
async fn rename(source: &Path, target: &Path) -> Result<bool, String> {
    if check_exist(source).await.is_err() {
        return Err("Source path does not exist".to_string());
    }
    if check_exist(target).await.is_ok() {
        return Err("Target path already exist".to_string());
    }
    return match fs::rename(source, target) {
        Ok(_) => Ok(true),
        Err(_) => Err("File/folder rename failed".to_string()),
    };
}

/// 文件/文件夹复制
#[tauri::command]
async fn copy(source: &Path, target: &Path) -> Result<bool, String> {
    if source.is_file() {
        return match fs::copy(source, target) {
            Ok(_) => Ok(true),
            Err(_) => Err("Copy file failed".to_string()),
        };
    }
    if source.is_dir() {
        return match XDirectory::copy(source, target) {
            Ok(_) => Ok(true),
            Err(_) => Err("Copy directory failed".to_string()),
        };
    }
    return Err("Copy path failed".to_string());
}

/// 计算文件的MD5
#[tauri::command]
async fn file_md5(path: &Path) -> Result<String, String> {
    if path.is_file() {
        let mut digest = Md5::new();

        let file: File = File::open(path).expect("Unable to open file");
        let mut reader: BufReader<File> = BufReader::new(file);
        // 读取文件, 每次读取1024字节
        let mut buffer: [u8; 1024] = [0; 1024];
        loop {
            let read_count = reader.read(&mut buffer).unwrap();
            if read_count == 0 {
                break;
            } else {
                digest.update(&buffer[..read_count]);
            }
        }
        let md5 = DigestFmt(digest.result()).to_string();
        return Ok(md5);
    }
    return Err("The path is not a file".to_string());
}

pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
    tauri::plugin::Builder::new("file-extra")
        .invoke_handler(tauri::generate_handler![
            state,
            check_exist,
            ensure_exist,
            remove,
            rename,
            copy,
            file_md5
        ])
        .build()
}
